﻿using System;

namespace CodeRecipes
{
    public class CircularQueue<T>
    {
        private T[] _buffer; 
        private int _length;    // size of buffer
        private int _head = -1; // dequeue pointer
        private int _tail = -1; // enqueue pointer
        private static readonly object lockObj = new object();

        private void HandleQueueFull()
        {
            Console.WriteLine("Queue is full.");
        }

        private void HandleQueueEmpty()
        {
            Console.WriteLine("Queue is empty.");
        }

        public CircularQueue(int bufferSize_)
        {
            _buffer = new T[bufferSize_];
            _length = bufferSize_;
        }

        public bool Enqueue(T item_)
        {
            lock (lockObj)
            {
                bool result = false;
                if ((_head == 0 && _tail == _length - 1) ||
                    (_tail + 1 == _head))
                {
                    HandleQueueFull();
                    return result;
                }

                if (_tail == _length - 1)
                {
                    _tail = 0;
                }
                else
                {
                    _tail++;
                }

                _buffer[_tail] = item_;

                if (_head == -1)
                {
                    _head = 0;
                }

                return true;
            }
        }

        public T Dequeue()
        {
            lock (lockObj)
            {
                T result = default(T);

                if (_head == -1)
                {
                    HandleQueueEmpty();
                    return result;
                }

                result = _buffer[_head];
                _buffer[_head] = default(T);

                if (_head == _tail)
                {
                    _head = _tail = -1; // queue is empty now.
                }
                else if (_head == _length - 1)
                {
                    _head = 0;
                }
                else
                {
                    _head++;
                }

                return result;
            }
        }

        public void Show()
        {
            lock (lockObj)
            {
                for (int i = 0; i < _length; i++)
                {
                    Console.Write("{0} ",
                        (_buffer[i] != null ?
                        _buffer[i].ToString() : " "));
                }
                Console.WriteLine();
            }
        }
    }
}
